/* 
 * ***** BEGIN LICENSE BLOCK ***** Version: NPL 1.1/GPL 2.0/LGPL 2.1 The
 * contents of this file are subject to the Netscape Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at http://www.mozilla.org/NPL/
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License. The
 * Original Code is lutsko.com code. The Initial Developer of the Original Code
 * is James Lutsko. Portions created by the Initial Developer are Copyright (C)
 * 2003 the Initial Developer. All Rights Reserved. Contributor(s):
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or the
 * GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which
 * case the provisions of the GPL or the LGPL are applicable instead of those
 * above. If you wish to allow use of your version of this file only under the
 * terms of either the GPL or the LGPL, and not to allow others to use your
 * version of this file under the terms of the NPL, indicate your decision by
 * deleting the provisions above and replace them with the notice and other
 * provisions required by the GPL or the LGPL. If you do not delete the
 * provisions above, a recipient may use your version of this file under the
 * terms of any one of the NPL, the GPL or the LGPL. ***** END LICENSE BLOCK
 * *****
 */

package autoteam;



import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;

import db.ColumnData;
import db.DBRecord;
import db.DataAccessObject;

/**
 * The League object holds references to all of the actors: teams, children, etc. 
 * Its role is to put these together so as to create the team allocations.
 * 
 */

public class League implements Serializable, DocumentListener
{
    
    /** Local copy of the children */
    Vector children2 = null;
    
    public PlayerData player = null;
    
    /**
     * A vector holding the teams.
     * 
     * @see Team
     */
    Vector teams = null;
    
    /**
     * Holds relations between source data and internal representation.
     * 
     * @see DataMap
     */
    Skill dataMap = null;
    
    /** Drafts the teams. */
    public Drafter drafter = null;
    
    transient public File sourceFile = null;
    transient boolean _isDirty = false;
    
    public Vector carPools = null;
    public Vector sibs = null;
    Double[] AverageSkill = null;
    Integer AverageHeight = null;
    public String league = null;

    /* This will load a new league from a given file */
    /*
    public League( File inputFile, DataMap _dataMap )
    {
	initialize( _dataMap );
	
	try
	{
	    FileReader in = new FileReader( inputFile );
	    BufferedReader rd = new BufferedReader( in );
	    String input;
	    
	    league = inputFile.getName( ).split( "\\." )[0];
	    
	    rd.readLine( );
	    while ( (input = rd.readLine( )) != null )
	    {
		Child o = new Child( input, dataMap );
		children2.add( o );
	    }
	    in.close( );
	}
	catch ( java.io.IOException e )
	{
	};
	
	finishUp( );
	
    }
    
    public League( File inputFile, Sheet s, DataMap _dataMap )
    {
	initialize( _dataMap );
	
	league = inputFile.getName( ).split( "\\." )[0];
	int pos = 1;
	
	for ( int i = 1; i < s.getRows( ); i++ )
	{
	    Child o = new Child( s.getRow( i ), dataMap );
	    children2.add( o );
	}
	
	finishUp( );
    }*/
    
    public League(File file) {													// added
    	try {
    		FileReader in = new FileReader(file);
    		BufferedReader rd = new BufferedReader(in);
    		league = file.getName().split("\\.")[0];
    		
    		String headers = rd.readLine();
    		
    		Skill _dataMap = new Skill(headers);
    		
    		initialize(_dataMap);
    		
    		String input;
    		
    		//find out if these children are already in there
    		DataAccessObject dao = new DataAccessObject();
    		dao.connect();
    		while ((input = rd.readLine()) != null) {
    			Child o = new Child(input, headers);
    			children2.add(o);
    			DBRecord temp = dao.getRecord("players", new ColumnData("playerId",o.id));
    			if (temp.getFieldValue("id") == null)
    				player.addPlayer(o);
    		}
    		in.close();
    		dao.disconnect();
    		
    	} catch (java.io.IOException e) {};
    	
    	finishUp();
    }
    
    public void setLeagueName( String s )
    {
	league = s;
    }
    
    public String getLeagueName( )
    {
	return league;
    }
    
    public transient DefaultStyledDocument logDocument = null;
    Team unassignedTeam = null;
    
    // Used only for serialization
    String logString;
    
    boolean doCarPool = true;
    
    static final long serialVersionUID = -3379283297329518746L;
    
    public Child getChild( int i )
    {
	return (Child) children2.get( i );
    }
    
    public void removeChild( int i )
    {
	children2.removeElementAt( i );
    }
    
    public Vector getChildren( )
    {
	return children2;
    }
    
    public int getNumberOfChildren( )
    {
	return children2.size( );
    }
    
    public int getNumberOfChildrenOnTeam( int j )
    {
	return ((Team) teams.get( j )).children.size( );
    }
    
    public String getTeamHeaderString( int nt, int displaySkill )
    {
	if ( nt == teams.size( ) )
	    return "Unassigned";
	
	Team t = (Team) teams.elementAt( nt );
	if ( t.children.size( ) > 0 )
	    return t.name + "(" + Nights.get( t.night ) + ") "
		   + Local.format( t.skill[displaySkill].doubleValue( ) / t.children.size( ) );
	return t.name;
    }
    
    public Child getKid( int nt, int nc )
    {
	Team t = (Team) (nt < teams.size( ) ? teams.elementAt( nt ) : unassignedTeam);
	if ( t == null )
	    return null;
	Child c = t.getChild( nc );
	if ( c != null )
	{
	    if ( c == t.coachKid )
		c.coachKid = true;
	    else
		c.coachKid = false;
	}
	return c;
    }
    
    public int getDraftType( )
    {
	return drafter.draftType;
    }
    
    public void setDraftType( int t )
    {
	drafter.draftType = t;
	setDirty( );
    }
    
    public int getNumberOfCriteria( )
    {
	return drafter.getCriteria( ).size( );
    }
    
    public void addCriterion( )
    {
	drafter.getCriteria( ).add( new DraftCriterion( 0, 0, 0, 0 ) );
	setDirty( );
    }
    
    public void removeCriterion( int r )
    {
	drafter.getCriteria( ).remove( r );
	setDirty( );
    }
    
    public boolean getDoCarPool( )
    {
	return doCarPool;
    }
    
    public void setDoCarPool( boolean b )
    {
	doCarPool = b;
	setDirty( );
    }
    
    public int getNumberOfSkillFields( )
    {
	return dataMap.getNumberOfSkills( );
    }
    
    public String getSkillName( int i )
    {
	return dataMap.getSkillName( i );
    }
    
    public void setSkill( DraftCriterion dc, String value, int tie )
    {
	if ( tie == 0 )
	    dc.Skill = null;
	else
	    dc.Tie = null;
	if ( value != null )
	{
	    int s = dataMap.getSkillIndex( value.toString( ) );
	    if ( s >= 0 )
	    {
		if ( tie == 0 )
		    dc.Skill = new Integer( s );
		else
		    dc.Tie = new Integer( s );
	    }
	}
	setDirty( );
    }
    
    public void setDirty( )
    {
	_isDirty = true;
    }
    
    public void clearDirty( )
    {
	_isDirty = false;
    }
    
    public boolean isDirty( )
    {
	return _isDirty;
    }
    
    public void addTeam(String teamName)
    {
    	if (teams == null)
    		teams = new Vector();
    	teams.add(new Team(teamName));
    	setDirty();
    }
    
    public void addTeam( )
    {
	if ( teams == null )
	    teams = new Vector( );
	teams.add( new Team( league + Integer.toString( teams.size( ) + 1 ) ) );
	setDirty( );
    }
    
    public Child newChild( )
    {
	Child c = new Child( dataMap.getNumberOfSkills( ) );
	children2.add( c );
	setDirty( );
	return c;
    }
    
    public void appendToLog( String str )
    {
	if ( logDocument != null )
	{
	    try
	    {
		logDocument.insertString( logDocument.getLength( ), str + Local.getNewLine( ), null );
		setDirty( );
	    }
	    catch ( BadLocationException e )
	    {
	    }
	}
    };
    
    public void deleteTeam( int t )
    {
	if ( teams == null )
	    return;
	if ( t > -1 && t < teams.size( ) )
	{
	    teams.removeElementAt( t );
	    setDirty( );
	}
    }
    
    public int getNumberOfTeams( )
    {
	if ( teams == null )
	    return 0;
	return teams.size( );
    }
    
    public Team getTeam( int t )
    {
	if ( t < 0 || teams == null || t >= teams.size( ) )
	    return null;
	return (Team) teams.get( t );
    }
    
    public int numberOfChildren( )
    {
	if ( children2 == null )
	    return 0;
	return children2.size( );
    }
    
    public void setAverageSkill( )
    {
	AverageSkill = new Double[dataMap.getNumberOfSkills( )];
	
	for ( int i = 0; i < AverageSkill.length; i++ )
	{
	    double skill = 0;
	    double num = 0.0;
	    for ( Iterator I = children2.iterator( ); I.hasNext( ); )
	    {
		Child c = (Child) I.next( );
		if ( (c.skill != null) && (c.skill[i] != null) && (c.skill[i].doubleValue( ) > 0.01) )
		{
		    num++;
		    skill += c.skill[i].intValue( );
		}
	    }
	    AverageSkill[i] = new Double( num == 0 ? 0 : skill / num );
	}
	
    }
    
    public Integer averageHeight( )
    {
	double height = 0.0;
	double num = 0.0;
	for ( Iterator I = children2.iterator( ); I.hasNext( ); )
	{
	    Child c = (Child) I.next( );
	    if ( (c.height != null) && (c.height.doubleValue( ) > 1.0) )
	    {
		num++;
		height += c.height.intValue( );
	    }
	}
	return new Integer( (int) (height / num) );
    }
    
    public void missingValues( )
    {
	setAverageSkill( );
	AverageHeight = averageHeight( );
	
	for ( Iterator I = children2.iterator( ); I.hasNext( ); )
	{
	    Child c = (Child) I.next( );
	    if ( c.skill == null )
		c.skill = new Double[dataMap.getNumberOfSkills( )];
	    c.totalSkill = 0.0;
	    for ( int i = 0; i < dataMap.getNumberOfSkills( ); i++ )
	    {
		if ( (c.skill[i] == null) || (c.skill[i].doubleValue( ) <= 0.01) )
		    c.skill[i] = AverageSkill[i];
		c.totalSkill += c.skill[i].doubleValue( );
	    }
	    if ( (c.height == null) || (c.height.doubleValue( ) < 1.0) )
		c.height = AverageHeight;
	}
    }
    
    public void guessSibs( )
    {
	Child c = null;
	for ( Iterator I = children2.iterator( ); I.hasNext( ); )
	{
	    if ( c == null )
		c = (Child) I.next( );
	    if ( I.hasNext( ) )
	    {
		Child next = (Child) I.next( );
		if ( c.lastName.matches( next.lastName ) )
		{
		    sibs.add( new ChildPair( c, next ) );
		    setDirty( );
		}
		c = next;
	    }
	}
    }
    
    public void guessCarPools( )
    {
	for ( Iterator I = children2.iterator( ); I.hasNext( ); )
	{
	    Child c = (Child) I.next( );
	    if ( c.comment != null && c.comment.trim( ).length( ) > 0 )
	    {
		Child cp = null;
		for ( Iterator J = children2.iterator( ); J.hasNext( ) && (cp == null); )
		{
		    Child c1 = (Child) J.next( );
		    if ( c.comment.toLowerCase( ).indexOf( c1.lastName.toLowerCase( ) ) >= 0 )
			cp = c1;
		}
		carPools.add( new ChildPair( c, cp ) );
		setDirty( );
	    }
	}
    }
    
    Child getUnassigned( int pos )
    {
	return drafter.getUnassigned( pos );
    }
    
    public void HarmonizeNights( )
    {
	// Initialize
	for ( Iterator J = children2.iterator( ); J.hasNext( ) && doCarPool; )
	{
	    // ((Child) J.next()).initializeNights();
	    Child c = (Child) J.next( );
	    c.initializeNights( );
	}
	// Multiple iterations are used because we may have chains of
	// carpools { (c1,c2) (c2,c3) ...} which must be harmonized.
	boolean stop = true;
	do
	{
	    stop = true;
	    for ( Iterator J = children2.iterator( ); J.hasNext( ) && doCarPool; )
	    {
		Child c = (Child) J.next( );
		for ( Iterator I = carPools.iterator( ); I.hasNext( ); )
		{
		    ChildPair cp = (ChildPair) I.next( );
		    if ( !cp.active )
			continue;
		    Child c1 = (Child) cp.child1;
		    Child c2 = (Child) cp.child2;
		    if ( c1 == null || c2 == null )
			continue;
		    
		    for ( int i = 0; i <= 4; i++ )
		    {
			if ( c1.nightAgreesWith( c2, i ) )
			    continue;
			stop = false;
			c1.setNight( i, Boolean.FALSE );
			c2.setNight( i, Boolean.FALSE );
		    }
		}
	    }
	}
	while ( !stop );
    }
    
    public void draftTeams( )
    {
	missingValues( );
	HarmonizeNights( );
	drafter.initializeDraft( children2, carPools, sibs, teams, doCarPool );
	drafter.doDraft( teams, this );
	drafter.unassigned( unassignedTeam, this );
	
	// Cleanup : pack the teams
	for ( Iterator I = teams.iterator( ); I.hasNext( ); )
	{
	    Team t = (Team) I.next( );
	    while ( t.children.remove( null ) )
	    {
	    };
	}
	String msg = "Unassigned children: " + Integer.toString( drafter.getNumberUnassigned( ) ) + Local.getNewLine( );
	drafter.recordUnassigned( msg );
	
	for ( Iterator I = children2.iterator( ); I.hasNext( ); )
	{
	    Child c = (Child) I.next( );
	    if ( c.hasTeam( ) )
		if ( !c.checkNight( c.getTeam( ) ) )
		    msg += "Bad assingment:" + c.firstName + " " + c.lastName + Local.getNewLine( );
	}
	setDirty( );
	appendToLog( msg );
	return;
    }
    
    public String CheckSwitch( int t1, int i1, int t2, int i2 )
    {
	Team team1 = (Team) (t1 < teams.size( ) ? teams.get( t1 ) : unassignedTeam);
	Team team2 = (Team) (t2 < teams.size( ) ? teams.get( t2 ) : unassignedTeam);
	Child c1 = null;
	Child c2 = null;
	
	if ( team1.children.size( ) > i1 )
	    c1 = (Child) team1.children.get( i1 );
	if ( team2.children.size( ) > i2 )
	    c2 = (Child) team2.children.get( i2 );
	
	String msg = null;
	if ( c1 != null )
	{
	    if ( c1 == team1.coachKid )
	    {
		String msg1 = "Do you want to move the coach's kid for team " + team1.name + "?";
		if ( msg != null )
		    msg += Local.getNewLine( ) + msg1;
		else
		    msg = msg1;
	    }
	}
	if ( c2 != null )
	{
	    if ( c2 == team2.coachKid )
	    {
		String msg1 = "Do you want to move the coach's kid for team " + team2.name + "?";
		if ( msg != null )
		    msg += Local.getNewLine( ) + msg1;
		else
		    msg = msg1;
	    }
	}
	
	if ( c1 != null )
	    if ( !(team2 == unassignedTeam) && !c1.checkNight( team2 ) )
	    {
		String msg1 = c1 + " is not available on " + Nights.get( team2.night ) + " - move anyway?";
		if ( msg != null )
		    msg += Local.getNewLine( ) + msg1;
		else
		    msg = msg1;
	    }
	if ( c2 != null )
	    if ( !(team1 == unassignedTeam) && !c2.checkNight( team1 ) )
	    {
		String msg1 = c2 + " is not available on " + Nights.get( team1.night ) + " - move anyway?";
		if ( msg != null )
		    msg += Local.getNewLine( ) + msg1;
		else
		    msg = msg1;
	    }
	
	return msg;
    }
    
    public String Switch( int t1, int i1, int t2, int i2 )
    {
	Team team1 = (Team) (t1 < teams.size( ) ? teams.get( t1 ) : unassignedTeam);
	Team team2 = (Team) (t2 < teams.size( ) ? teams.get( t2 ) : unassignedTeam);
	Child c1 = null;
	Child c2 = null;
	
	if ( team1.children.size( ) > i1 )
	    c1 = (Child) team1.children.get( i1 );
	if ( team2.children.size( ) > i2 )
	    c2 = (Child) team2.children.get( i2 );
	
	if ( c1 == null && c2 == null )
	{
	    return "No children to trasfer";
	}
	
	if ( c1 == null )
	{
	    team2.removeChild( c2 );
	    team1.addChild( c2 );
	    appendToLog( "Player " + c2.toString( ) + " moved from " + team2.name + " to " + team1.name );
	}
	else if ( c2 == null )
	{
	    team1.removeChild( c1 );
	    team2.addChild( c1 );
	    appendToLog( "Player " + c1.toString( ) + " moved from " + team1.name + " to " + team2.name );
	}
	else
	{
	    team1.replaceChild( c1, c2 );
	    appendToLog( "Player " + c1.toString( ) + " moved from " + team1.name + " to " + team2.name );
	    team2.replaceChild( c2, c1 );
	    appendToLog( "Player " + c2.toString( ) + " moved from " + team2.name + " to " + team1.name );
	}
	setDirty( );
	return null;
	
    }
    
    public void saveDoc( File f )
    {
	try
	{
	    FileOutputStream out1 = null;
	    if ( f != null )
		out1 = new FileOutputStream( f );
	    else
		out1 = new FileOutputStream( league + ".rtb" );
	    
	    ObjectOutputStream s = new ObjectOutputStream( out1 );
	    s.writeObject( this );
	    clearDirty( );
	    s.flush( );
	}
	catch ( Exception e )
	{
	    ;
	}
    }
    
    public File writeLog( )
    {
	String dir = System.getProperty( "user.dir" );
	try
	{
	    File f = new File( dir, league + "_log" + ".txt" );
	    f.createNewFile( );
	    BufferedWriter out = new BufferedWriter( new FileWriter( f ) );
	    try
	    {
		out.write( logDocument.getText( 0, logDocument.getLength( ) ) );
	    }
	    catch ( javax.swing.text.BadLocationException e )
	    {
		System.out.println( e );
	    }
	    out.close( );
	    return f;
	}
	catch ( IOException e )
	{
	    System.out.println( e );
	}
	return null;
    }
    
    public void exportTeams( )
    {
	String dir = System.getProperty( "user.dir" );
	try
	{
	    for ( Iterator I = teams.iterator( ); I.hasNext( ); )
	    {
		Team t = (Team) I.next( );
		File f = new File( dir, league + "_" + t.name + ".txt" );
		if ( !f.exists( ) )
		    f.createNewFile( );
		BufferedWriter out = new BufferedWriter( new FileWriter( f ) );
		out.write( "Team Name: " + t.name );
		out.newLine( );
		out.write( "Coach    : " + t.coach );
		out.newLine( );
		out.write( "Practice : " + Nights.get( t.night ) );
		if ( t.startTime != null )
		    out.write( " " + t.startTime );
		if ( t.location != null )
		    out.write( " at " + t.location );
		out.newLine( );
		
		for ( Iterator I1 = t.children.iterator( ); I1.hasNext( ); )
		{
		    Child c = (Child) I1.next( );
		    out.write( "\t" + c.export( ) );
		    out.newLine( );
		}
		out.close( );
	    }
	}
	catch ( IOException e )
	{
	    System.out.println( e );
	}
	
	writeLog( );
    }
    
    private void writeObject( java.io.ObjectOutputStream out ) throws IOException
    {
	try
	{
	    logString = logDocument.getText( 0, logDocument.getLength( ) - 1 );
	}
	catch ( javax.swing.text.BadLocationException e )
	{
	    System.out.println( e );
	}
	out.defaultWriteObject( );
    }
    
    private void readObject( java.io.ObjectInputStream in ) throws IOException, ClassNotFoundException
    {
	in.defaultReadObject( );
	logDocument = new DefaultStyledDocument( );
	logDocument.addDocumentListener( this );
	appendToLog( logString );
	clearDirty( );
    }
    
    public void changedUpdate( DocumentEvent e )
    {
	setDirty( );
    }
    
    public void insertUpdate( DocumentEvent e )
    {
	setDirty( );
    }
    
    public void removeUpdate( DocumentEvent e )
    {
	setDirty( );
    }
    
    public void checkTeams( )
    {
	for ( Iterator I = children2.iterator( ); I.hasNext( ); )
	{
	    Child c = (Child) I.next( );
	    if ( c.hasTeam( ) )
		if ( !c.checkNight( c.getTeam( ) ) )
		    appendToLog( "Bad assignment:" + c.firstName + " " + c.lastName );
	}
    }
    
    void initialize( Skill _dataMap )
    {
    	children2      = new Vector();
    	carPools       = new Vector();
    	sibs           = new Vector();
    	teams          = new Vector();
    	drafter        = new Drafter(_dataMap);
    	dataMap        = _dataMap;
    	unassignedTeam = new Team(Local.get("Unassigned"));
    	
    	String[] skillNames = new String[_dataMap.getNumberOfSkills()];
    	for (int i = 0; i < skillNames.length; i++)
    		skillNames[i] = _dataMap.getSkillName(i);
    	
    	player = new PlayerData(skillNames);
    	
    	logDocument = new DefaultStyledDocument();
    	logDocument.addDocumentListener(this);
    	appendToLog("Date: " + (new Date()));
    }
    
    void finishUp( )
    {
	// fill the log
	appendToLog( "Read " + Integer.toString( numberOfChildren( ) ) + " children" + Local.getNewLine( ) );
	missingValues( );
	setDirty( );
	
	// sort
	Collections.sort( children2, new Comparator( )
	{
	    public int compare( Object o1, Object o2 )
	    {
		return o1.toString( ).compareTo( o2.toString( ) );
	    }
	} );
    }
}
